/*
 * 2020/9/2	14:27	qing
 */

优化 optimizations


ILP 
    (Instruction-Level Parallelism, 指令级并行)

FLOPS 
    (Floating Point Operations Per Second，每秒浮点运算次数)

Software Pipelineing
    软件流水线(其实跟硬件流水线非常的像，本质上没太大区别)

Trace Scheduling
    该方法的关键思想是将基本块组合起来，使它们形成一个单入口多出口的较大块，可能作为直线代码执行。

basic block
    其实就是一段连续的代码，其中没有分支或者跳转指令，具有单一的入口和出口；
    
    由于控制密集型指令的存在(比如分支指令)，basic block的size不可能太大；

    对于单个basic block内部而言，很难挖掘出ILP(单个Basic block内部，认为是顺序执行的，指令与指令之间依赖关系较大)；

CPI
    Clock Per Instruction，即每条指令所需要的周期数，它反映了处理器并行处理指令个数的能力。
    有时候也用其倒数形式，即IPC(Instruction Per Clock)。

VLIW
    (Very Long/Large Instruction Word，超长指令字)
    将多条互相独立的指令，通过软件（编译器）的方式打包（Pack）在一起，将打包好的多条指令，称为instruction bundle。
    取指模块根据打包好的指令，送入各自独立的功能部件，并行执行

Superscalar
    超标量


/*
 * CAS
 */
    使用 CPU 原子性指令实现无锁操作

    compare and swap, 是所有CPU指令都支持CAS的原子操作。
    x86 :   cmpxchg, lock 两个指令
    

    // 关于CAS等原子操作
    https://coolshell.cn/articles/8239.html


/*
 * 单向链表中prev指针的妙用
 */
    为啥要pt_prev维护一个后向节点呢？   (/net/inet/dev.c)

        void net_bh(void *tmp)
    527 {
           ...
    589   pt_prev = NULL;
    590   for (ptype = ptype_base; ptype != NULL; ptype = ptype->next)
    591   {
    592    if ((ptype->type == type || ptype->type == htons(ETH_P_ALL)) && (!ptype->dev || ptype->dev==skb->dev))
    593    {
    594      /*
    595      * We already have a match queued. Deliver
    596      * to it and then remember the new match
    597      */
    598      if(pt_prev)
    599      {
    600        struct sk_buff *skb2;
    601        skb2=skb_clone(skb, GFP_ATOMIC);
    602        /*
    603        * Kick the protocol handler. This should be fast
    604        * and efficient code.
    605        */
    606        if(skb2)
    607          pt_prev->func(skb2, skb->dev, pt_prev);
    608      }
    609      /* Remember the current last to do */
    610      pt_prev=ptype;
    611    }
    612   } /* End of protocol list loop */
    613   /*
    614   * Is there a last item to send to ?
    615   */
    616   if(pt_prev)
    617     pt_prev->func(skb, skb->dev, pt_prev);
    618   /*
    619    *  Has an unknown packet has been received ?
    620    */
    621   else
    622     kfree_skb(skb, FREE_WRITE);

    由于从网络上拷贝过来skb是唯一的，而分发的协议对象可能是多个，
        所以在回调之前要做一次clone动作（注意这里是深度拷贝，相当于一次kmalloc）。
        分发之后还需要调用kfree_skb释放掉原始skb数据块，它的历史使命到此完成了，
        没有保留的必要（第622行）。注意，这两个动作都是存在内核开销的。

    它的作用就是将当前匹配协议项的回调操作延时了。举个例子，如果链表遍历中找到某个匹配项，
    当前循环仅仅用pt_prev去记录这个匹配项，除此之外不做任何事情，待到下一次匹配项找到时，
    才去做上一个匹配项pt_prev的回调操作，直到循环结束，
    才会去做最后的匹配项的回调（当然pt_prev==NULL表示没有一次匹配，直接释放掉），所以这是一种拖延战术。
    
    有什么好处呢？就是比原先节省了很多不必要的操作。那么哪些操作是不必要的呢？
    这里逆向思考一下，看到clone是在协议字段匹配并且pt_prev!=NULL的前提条件下执行的，
    而kfree是在pt_prev==NULL的前提条件下执行的。在此可以假设一下，如果ptype链表中存在N项协议与之匹配，
    那么这段代码只会执行N-1次clone，而没有pt_prev时将会执行N次clone和1次kfree，
    再如果ptype链表中有且仅有一项协议与之匹配，那么整个循环既不会执行到第601行的clone，也不会执行到第622行的kfree。

    也就是说，当整个链表至少有一项匹配的一般情况下，pt_prev存在比没有时减少了一次clone和一次kfree的开销；
    只有全部不匹配的最差情况下，两者都只做一次kfree动作，持平。这就是延迟策略产生的效益。

    https://coolshell.cn/articles/9859.html

/*
 * printf
 */
    printf("-");    // fork 时printf(“-“)语句有buffer，会有多输出

    程序遇到"\n"，或是EOF，或是缓中区满，或是文件描述符关闭，或是主动flush，或是程序退出，就会把数据刷出缓冲区。

    scanf中的”%[^r]”是遇到字符r就结束输入。









// NEON加速之memcpy在ARM平台的优化
https://www.jianshu.com/p/7b3bfc3aed12

//《计算机体系结构：量化研究方法》第五章 Thread-Level Parallelism线程级并行TLP
https://zhuanlan.zhihu.com/p/707220951

// 与程序员相关的CPU缓存知识
https://coolshell.cn/articles/20793.html

// 浅谈Cache Memory
http://www.wowotech.net/memory_management/458.html

// 内存对齐讨论
https://www.jianshu.com/p/55614ccb3eda

// ios获取cpu的实时频率(深入到汇编、硬件参数层)
https://www.jianshu.com/p/b8d55ec7659d

// 性能调优攻略
https://coolshell.cn/articles/7490.html
https://coolshell.cn/articles/2967.html

// Linus：利用二级指针删除单向链表
https://coolshell.cn/articles/8990.html


// 一个fork的面试题
https://coolshell.cn/articles/7965.html




